/*
 * RevenueSourceDAO.java
 *
 * Created on March 5, 2007, 8:11 PM
 *
 */

package edu.byu.isys413.group1E.data;

import edu.byu.isys413.group1E.*;
import java.sql.*;
import java.util.ArrayList;

/** This is a public abstract class through which the multiple types of revenue sources will be accessed.
 *
 * @author Travis Kulbeth
 */
public abstract class RevenueSourceDAO {
    
    /** Contains a singleton instance of RevenueSourceDAO*/
    //private static RevenueSourceDAO instance = null;

    /**Retrieves an instance of the TransactionDAO object.
     * This method is overridden in all subclasses, but is defined here to enable
     * the other classes getInstance methods to be accessed.
     *
     * @return instance
     */
//    public RevenueSourceDAO getInstance(){
//        return instance;
//    }

    
///////////////////////////////////////////////////////////////////////////////
    /////////   CREATE
    /**
     * Abstract method to require every extending class to have a create method.
     * 
     * @return a newly created RevenueSourceBO with unique GUID
     * @throws java.lang.Exception to catch any errors in the creation process.
     */
    public abstract RevenueSourceBO create() throws Exception;

    //////////////////////////////////////////////////////////////////////////////
    //////////   READ
    /**
     * This is the public abstract read statement.  It requires all extending classes to have a 
     * read method to load an existing RevenueSourceBO record
     * from the database based on the RevSrcID and return that object.
     * 
     * @return a RevenueSourceBO containing the information extracted from the requested
     * table and tuple in the DB.
     * @param id contains the unique identifier for the record to be read from the DB.
     * @throws edu.byu.isys413.group1E.DataException to catch any errors with the incoming data
     * @throws edu.byu.isys413.group1E.ConnectionPoolException to catch errors when connecting to the DB
     */
    public abstract RevenueSourceBO read(String id) throws SQLException, DataException, ConnectionPoolException;
    
    
    /**
     * This is a public abstract read statement that is called after obtaining
     * a connection with the DB.  It loads an existing RevenueSource record
     * from the database based on the RevSrcID given, returning the requested BO.
     *
     * The subclasses to this class call this method, which reads the object from the cache, 
     * reads the data from the Revenue_Source table that is universal across revenue sources, then 
     * puts the object back in the cache after updating it with the info.
     * 
     * @return RevenueSourceBO
     * @param id contains the given id for the Employee to be read
     * @param conn contains an already open connection to the DB
     * @throws edu.byu.isys413.group1E.ConnectionPoolException catches any errors with the connection
     * @throws edu.byu.isys413.group1E.DataException catches any errors with incoming data
     * @throws java.sql.SQLException catches any errors from the SQL execution
     */   
    public synchronized RevenueSourceBO read(String id, Connection conn) throws SQLException, DataException, ConnectionPoolException{
        // check to see if id in the cache
        Cache c = Cache.getInstance();
        RevenueSourceBO revSrc = (RevenueSourceBO)c.get(id);
        
        // if not in the cache, get a result set from
        // a SELECT * FROM table WHERE id=guid
        Statement stmt = conn.createStatement();
        ResultSet rs = stmt.executeQuery("SELECT * FROM Revenue_Source WHERE RevenueSourceID = '" + id + "'");
        conn.commit();
        
        //build the object from the result set
        if (rs.next()) {
            revSrc.setRevSrcID(rs.getString("RevenueSourceID"));
            revSrc.setDescription(rs.getString("Description"));
            revSrc.setPrice(rs.getDouble("Price"));
            revSrc.setType(rs.getString("TypeRS"));
            revSrc.setAlreadyInDB(true);
            revSrc.setIsDirty(false);
            
            // save to the cache
            c.put(revSrc.getRevSrcID(), revSrc);
        }else{
            throw new DataException("Object was not found in the database.");
        }
        stmt.close();
        // return the BO
        return revSrc;
    }
    
    /*Retrieve list of all revSrcomers*/
    public abstract ArrayList<RevenueSourceBO> readAll() throws DataException, SQLException, ConnectionPoolException;
//    {
//        ArrayList<RevenueSourceBO> revSrcList = new ArrayList<RevenueSourceBO>();
//        //now build the query
//        String query = "SELECT * FROM Revenue_Source";
//
//        Connection conn = ConnectionPool.getInstance().get();
//        Statement stmt = conn.createStatement();
//        ResultSet rs = stmt.executeQuery(query);
//        conn.commit();
//        
//        //extract the elements from the result set and build a list of results
//        //build the object from the result set
//        while (rs.next()) {
//            RevenueSourceBO revSrc = new RevenueSourceBO(rs.getString("RevenueSourceID"));
//            revSrc.setDescription(rs.getString("Description"));
//            revSrc.setAlreadyInDB(true);
//            revSrc.setIsDirty(false);
//            
//            // save to the list
//            revSrcList.add(revSrc);
//        }//while
//        
//        stmt.close();
//        ConnectionPool.getInstance().release(conn);
//        return revSrcList;
//    }
    
    //////////////////////////////////
    ///   UPDATE
    
    /**
     * This is the public save method.  It is what is called when
     * the user (controller) code wants to save or update an object
     * into the database.
     */
    public synchronized void save(RevenueSourceBO revSrcIn) throws DataException, ConnectionPoolException{
        try {
            Connection conn = ConnectionPool.getInstance().get();
            try {
                this.save(revSrcIn, conn);                                   // call save with a connection
                conn.commit();
            } catch (ConnectionPoolException ex) {
                conn.rollback();
                ex.printStackTrace();
                throw new SQLException();
            } catch (SQLException ex) {
                conn.rollback();
                ex.printStackTrace();
            } catch (DataException ex) {
                conn.rollback();
                ex.printStackTrace();
            }
            ConnectionPool.getInstance().release(conn);                       // release the connection
        }catch (SQLException e) {
            throw new DataException("Error saving record for RevenueSourceID =" + revSrcIn.getRevSrcID(), e);
        }catch (ConnectionPoolException x){
            throw new ConnectionPoolException("Could not establish connection to database.", x);
        }
        
        // release the connection back to the pool
        return;
    }
    
    /**
     *  This is a package method that is called by the public save (above) or
     *  by another DAO.  Either way we already have a connection to the database
     *  to use.  The user (controller) code never calls this one directly, since
     *  it can't know about Connection objects or SQLExceptions.
     *
     *  By having other DAOs call this save method (rather than update or
     *  insert below, each DAO in a chained save (like the RevenueSourceDAO calling
     *  MembershipDAO calling InterestDAO save chain) can independently decide
     *  whether to udpate or insert the BO it's saving.  That's why I made
     *  update and insert private rather than package level -- if you call
     *  them directly from another DAO, this DAO can't decide whether it's
     *  object needs to be inserted or updated.
     */
    public synchronized void save(RevenueSourceBO revSrc, Connection conn) throws SQLException, DataException, ConnectionPoolException {
        // run update or insert
        if (revSrc.isIsDirty()) {
            if (revSrc.isAlreadyInDB()) {
                updateRS(revSrc, conn);
                update(revSrc, conn);
            }else{
                insertIntoRS(revSrc, conn);
                insert(revSrc, conn);
            }
            // set the dirty flag to false now that we've saved it
            revSrc.setIsDirty(false);
        }

                
        // touch the cache for the object
        Cache c = Cache.getInstance();
        c.put(revSrc.getRevSrcID(), revSrc);
    }

    private void updateRS(RevenueSourceBO revSrc, Connection conn) throws SQLException, DataException, ConnectionPoolException{
        PreparedStatement pstmt = conn.prepareStatement("UPDATE Revenue_Source SET Description = ?, Price = ?, TypeRS = ? " +
                "WHERE RevenueSourceID = ?");
        pstmt.setString(1, revSrc.getDescription());
        pstmt.setDouble(2, revSrc.getPrice());
        pstmt.setString(3, revSrc.getType());
        pstmt.setString(4, revSrc.getRevSrcID());        
        pstmt.execute();
    }
    
    
    private void insertIntoRS(RevenueSourceBO revSrc, Connection conn) throws SQLException, DataException, ConnectionPoolException{
        PreparedStatement pstmt = conn.prepareStatement("INSERT INTO Revenue_Source (RevenueSourceID, " +
                "Description, Price, TypeRS) VALUES (?, ?, ?, ?)");
        pstmt.setString(1, revSrc.getRevSrcID());
        pstmt.setString(2, revSrc.getDescription());
        pstmt.setDouble(3, revSrc.getPrice());
        pstmt.setString(4, revSrc.getType());
        pstmt.execute();
    }
    
    /**
     * This method is really a part of save(bo, conn) above.  It could be
     * embedded directly in it, but I've separated it into it's own method
     * to isolate the SQL udpate statement and make it more readable.  But
     * logically, it's really just a part of save.
     */
    abstract void update(RevenueSourceBO revSrc, Connection conn) throws SQLException, DataException;
//    {
//        //update the DB
//        PreparedStatement pstmt = conn.prepareStatement("UPDATE Revenue_Source SET Description = ? WHERE RevenueSourceID = ?");
//        pstmt.setString(1, revSrc.getDescription());
//        pstmt.setString(2, revSrc.getRevSrcID());
//        pstmt.executeUpdate();
//        pstmt.close();
//        revSrc.setAlreadyInDB(true);
//    }
    
    /**
     * This method is really a part of save(bo, conn) above.  It could be
     * embedded directly in it, but I've separated it into it's own method
     * to isolate the SQL insert statement and make it more readable.  But
     * logically, it's really just a part of save.
     */
    abstract void insert(RevenueSourceBO revSrc, Connection conn) throws SQLException, DataException;
//    {
//        //update the DB
//        PreparedStatement pstmt = conn.prepareStatement("INSERT INTO Revenue_Source (RevenueSourceID, Description)" +
//                "VALUES (?, ?)");
//        pstmt.setString(1, revSrc.getRevSrcID());
//        pstmt.setString(2, revSrc.getDescription());
//        pstmt.executeUpdate();
//        pstmt.close();
//        // tell the object that it's now in the db (so we call update next time not insert)
//        revSrc.setAlreadyInDB(true);
//    }
    
    
    ///////////////////////////////////////////////////////////////////////////////
    /////////   DELETE
    
    //I did not include the DELETE functionality intentionally due to instructions received, as well as 
    //to protect the integrity of the data.


}
